package node;

import lexer.LexType;
import parser.Parser;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class StmtNode extends ASTNode {
    public final String name = "<Stmt>";
    boolean isGetint = false;
    boolean isGetchar = false;
    boolean isBreak = false;
    boolean isContinue = false;
    boolean isReturn = false;
    boolean isIf = false;
    boolean isFor = false;
    /*
     * Stmt → LVal '=' Exp ';'
     * | [Exp] ';'
     * | Block
     * | 'if' '(' Cond ')' Stmt [ 'else' Stmt ]
     * | 'for' '(' [ForStmt] ';' [Cond] ';' [ForStmt] ')' Stmt
     * | 'break' ';' | 'continue' ';'
     * | 'return' [Exp] ';'
     * | LVal '=' 'getint''('')'';'
     * | LVal '=' 'getchar''('')'';'
     * | 'printf''('StringConst {','Exp}')'';'
     */
    private LValNode lValNode;
    private ExpNode expNode;
    private BlockNode blockNode;
    private CondNode condNode;
    private ForStmtNode forStmtNode1;
    private ForStmtNode forStmtNode2;
    private StmtNode stmtNode1;
    private StmtNode stmtNode2;
    private String stringConst;
    private List<ExpNode> expNodeList = new ArrayList<>();

    public StmtNode(LValNode lValNode, ExpNode expNode) {
        this.lValNode = lValNode;
        this.expNode = expNode;
    }

    public StmtNode(ExpNode expNode) {
        this.expNode = expNode;
    }

    public StmtNode(ExpNode expNode, boolean isReturn) {
        this.expNode = expNode;
        this.isReturn = isReturn;
    }

    public StmtNode(BlockNode blockNode) {
        this.blockNode = blockNode;
    }

    public StmtNode(CondNode condNode, StmtNode stmtNode1, StmtNode stmtNode2) {
        this.condNode = condNode;
        this.stmtNode1 = stmtNode1;
        this.stmtNode2 = stmtNode2;
        isIf = true;
    }

    public StmtNode(ForStmtNode forStmtNode1, CondNode condNode, ForStmtNode forStmtNode2, StmtNode stmtNode) {
        this.forStmtNode1 = forStmtNode1;
        this.condNode = condNode;
        this.forStmtNode2 = forStmtNode2;
        this.stmtNode1 = stmtNode;
        isFor = true;
    }

    public StmtNode(String stringConst, List<ExpNode> expNodeList) {
        this.stringConst = stringConst;
        this.expNodeList = expNodeList;
    }

    public StmtNode(LValNode lValNode, String getString) {
        this.lValNode = lValNode;
        if (getString.equals("getint")) {
            isGetint = true;
        } else if (getString.equals("getchar")) {
            isGetchar = true;
        }
    }

    public StmtNode(String type) {
        if (type.equals("break")) {
            isBreak = true;
        } else if (type.equals("continue")) {
            isContinue = true;
        }
    }

    @Override
    /*
     * Stmt →
     * LVal '=' Exp ';'
     * | LVal '=' 'getint''('')'';'
     * | LVal '=' 'getchar''('')'';'
     * | Block
     * | 'if' '(' Cond ')' Stmt [ 'else' Stmt ]
     * | 'for' '(' [ForStmt] ';' [Cond] ';' [ForStmt] ')' Stmt
     * | 'break' ';'
     * | 'continue' ';'
     * | 'return' [Exp] ';'
     * | 'printf''('StringConst {','Exp}')'';'
     * | [Exp] ';'
     */
    public void show() throws IOException {
        if (isLValAndExp()) {
            lValNode.show();
            Parser.parserWriter.write(LexType.ASSIGN + " " + "=" + "\n");
            expNode.show();
            Parser.parserWriter.write(LexType.SEMICN + " " + ";" + "\n");
        } else if (isGetint()) {
            lValNode.show();
            Parser.parserWriter.write(LexType.ASSIGN + " " + "=" + "\n");
            Parser.parserWriter.write(LexType.GETINTTK + " " + "getint" + "\n");
            Parser.parserWriter.write(LexType.LPARENT + " " + "(" + "\n");
            Parser.parserWriter.write(LexType.RPARENT + " " + ")" + "\n");
            Parser.parserWriter.write(LexType.SEMICN + " " + ";" + "\n");
        } else if (isGetchar()) {
            lValNode.show();
            Parser.parserWriter.write(LexType.ASSIGN + " " + "=" + "\n");
            Parser.parserWriter.write(LexType.GETCHARTK + " " + "getchar" + "\n");
            Parser.parserWriter.write(LexType.LPARENT + " " + "(" + "\n");
            Parser.parserWriter.write(LexType.RPARENT + " " + ")" + "\n");
            Parser.parserWriter.write(LexType.SEMICN + " " + ";" + "\n");
        } else if (isBlock()) {
            blockNode.show();
        } else if (isIf()) {
            Parser.parserWriter.write(LexType.IFTK + " " + "if" + "\n");
            Parser.parserWriter.write(LexType.LPARENT + " " + "(" + "\n");
            condNode.show();
            Parser.parserWriter.write(LexType.RPARENT + " " + ")" + "\n");
            stmtNode1.show();
            if (stmtNode2 != null) {
                Parser.parserWriter.write(LexType.ELSETK + " " + "else" + "\n");
                stmtNode2.show();
            }
        } else if (isFor()) {
            Parser.parserWriter.write(LexType.FORTK + " " + "for" + "\n");
            Parser.parserWriter.write(LexType.LPARENT + " " + "(" + "\n");
            if (forStmtNode1 != null) {
                forStmtNode1.show();
            }
            Parser.parserWriter.write(LexType.SEMICN + " " + ";" + "\n");
            if (condNode != null) {
                condNode.show();
            }
            Parser.parserWriter.write(LexType.SEMICN + " " + ";" + "\n");
            if (forStmtNode2 != null) {
                forStmtNode2.show();
            }
            Parser.parserWriter.write(LexType.RPARENT + " " + ")" + "\n");
            stmtNode1.show();
        } else if (isBreak()) {
            Parser.parserWriter.write(LexType.BREAKTK + " " + "break" + "\n");
            Parser.parserWriter.write(LexType.SEMICN + " " + ";" + "\n");
        } else if (isContinue()) {
            Parser.parserWriter.write(LexType.CONTINUETK + " " + "continue" + "\n");
            Parser.parserWriter.write(LexType.SEMICN + " " + ";" + "\n");
        } else if (isReturn()) {
            Parser.parserWriter.write(LexType.RETURNTK + " " + "return" + "\n");
            if (expNode != null) {
                expNode.show();
            }
            Parser.parserWriter.write(LexType.SEMICN + " " + ";" + "\n");
        } else if (isPrintf()) {
            Parser.parserWriter.write(LexType.PRINTFTK + " " + "printf" + "\n");
            Parser.parserWriter.write(LexType.LPARENT + " " + "(" + "\n");
            Parser.parserWriter.write(LexType.STRCON + " " + stringConst + "\n");
            for (ExpNode expNode : expNodeList) {
                Parser.parserWriter.write(LexType.COMMA + " " + "," + "\n");
                expNode.show();
            }
            Parser.parserWriter.write(LexType.RPARENT + " " + ")" + "\n");
            Parser.parserWriter.write(LexType.SEMICN + " " + ";" + "\n");
        } else {
            if (isExp()) {
                expNode.show();
            }
            Parser.parserWriter.write(LexType.SEMICN + " " + ";" + "\n");
        }
        Parser.parserWriter.write(name + "\n");
    }

    public boolean isReturn() {
        return isReturn;
    }

    public boolean isBreak() {
        return isBreak;
    }

    public boolean isContinue() {
        return isContinue;
    }

    public boolean isFor() {
        return isFor;
    }

    public boolean isIf() {
        return isIf;
    }

    public boolean isGetchar() {
        return isGetchar;
    }

    public boolean isGetint() {
        return isGetint;
    }

    public boolean isLValAndExp() {
        return lValNode != null && expNode != null;
    }

    public boolean isBlock() {
        return blockNode != null;
    }

    public boolean isPrintf() {
        return stringConst != null;
    }

    public boolean isExp() {
        return expNode != null && !isReturn() && !isLValAndExp();
    }

    public BlockNode getBlockNode() {
        return blockNode;
    }

    public CondNode getCondNode() {
        return condNode;
    }

    public ExpNode getExpNode() {
        return expNode;
    }

    public List<ExpNode> getExpNodeList() {
        return expNodeList;
    }

    public ForStmtNode getForStmtNode1() {
        return forStmtNode1;
    }

    public ForStmtNode getForStmtNode2() {
        return forStmtNode2;
    }

    public StmtNode getStmtNode1() {
        return stmtNode1;
    }

    public StmtNode getStmtNode2() {
        return stmtNode2;
    }

    public String getStringConst() {
        return stringConst;
    }

    public LValNode getlValNode() {
        return lValNode;
    }

}
